(function(_global) {
  'use strict';

  var topology = {
    init: init
  };

  function init(dom) {
    var topo = new Topology(dom);
    return topo;
  }

  function Topology(dom) {
    var zr = zrender.init(dom);

    addDragEvent(zr, this);
    addZoomEvent(zr, this);
    this.zrender = zr;
  }

  Topology.prototype.create = function(data) {
    var group = new zrender.Group({ position: [0, 0] });
    makeGraph(this, group, data);
    makeVectorLines(this, group, data);
    makeTip(this, group);

    this.group = group;
    this.zrender.add(group);
    console.log("页面加载结束：" + new Date().getTime())
  };

  Topology.prototype.on = function(eventClass, callback) {
    if (eventClass === 'click') {
      this.onclick = callback;
    }
  };

  function makeGraph(topology, group, data) {
    _.forEach(data, function(node) {
      var title = node.text.title;
      var hoverTextColor = node.text.hover.color;
      var hoverBgColor = node.bg.hover.color;

      var graph;
      if (node.nodeType == "case") {
        graph = new zrender.Polygon({
          nodeData: node.nodeData,
          shape: {
            points: [
              [node.x + node.width / 2, node.y],
              [node.x + node.width, node.y + node.height / 2],
              [node.x + node.width / 2, node.y + node.height],
              [node.x, node.y + node.height / 2]
            ]
          },
          style: {
            fill: node.bg.color,
            lineWidth: 0,
            shadowColor: '#a5b9cc',
            shadowBlur: 3,
            shadowOffsetY: 1,
            text: node.text.value,
            title: node.text.title,
            textFill: node.text.color,
            fontFamily: '微软雅黑',
            fontSize: '10px',
            textPosition: 'inside',
            textAlign: 'center',
            truncate: {
              outerWidth: node.width,
              outerHeight: node.height,
            }
          }
        });
      } else if (node.nodeType == "process") {
        graph = new zrender.Ellipse({
          nodeData: node.nodeData,
          shape: {
            cx: node.x + node.width / 2,
            cy: node.y + node.height / 2,
            rx: node.width / 2,
            ry: node.height / 2
          },
          style: {
            fill: node.bg.color,
            lineWidth: 0,
            shadowColor: '#a5b9cc',
            shadowBlur: 3,
            shadowOffsetY: 1,
            text: node.text.value,
            title: node.text.title,
            textFill: node.text.color,
            fontFamily: '微软雅黑',
            fontSize: '10px',
            textPosition: 'inside',
            textAlign: 'center',
            truncate: {
              outerWidth: node.width,
              outerHeight: node.height,
            }
          }
        });
      } else if (node.nodeType == "data") {
        graph = new zrender.Polygon({
          nodeData: node.nodeData,
          shape: {
            points: [
              [node.x + node.width / 4, node.y],
              [node.x + node.width, node.y],
              [node.x + node.width * 3 / 4, node.y + node.height],
              [node.x, node.y + node.height]
            ]
          },
          style: {
            fill: node.bg.color,
            lineWidth: 0,
            shadowColor: '#a5b9cc',
            shadowBlur: 3,
            shadowOffsetY: 1,
            text: node.text.value,
            title: node.text.title,
            textFill: node.text.color,
            fontFamily: '微软雅黑',
            fontSize: '10px',
            textPosition: 'inside',
            textAlign: 'center',
            truncate: {
              outerWidth: node.width,
              outerHeight: node.height,
            }
          }
        });
      } else {
        graph = new zrender.Rect({
          nodeData: node.nodeData,
          shape: {
            x: node.x,
            y: node.y,
            width: node.width,
            height: node.height,
            r: [2, 2, 2, 2]
          },
          style: {
            fill: node.bg.color,
            lineWidth: 0,
            shadowColor: '#a5b9cc',
            shadowBlur: 3,
            shadowOffsetY: 1,
            text: node.text.value,
            title: node.text.title,
            textFill: node.text.color,
            fontFamily: '微软雅黑',
            fontSize: '10px',
            textPosition: 'inside',
            textAlign: 'center',
            truncate: {
              outerWidth: node.width,
              outerHeight: node.height,
            }
          }
        });
      }


      graph.on('mousedown', function(params) {
        if (topology.onclick) {
          topology.onclick(params);
        }
      })

      graph.on('mouseover', function(params) {
        graph.attr({
          shape: {
            x: node.x - 3,
            y: node.y - 3,
            width: node.width + 6,
            height: node.height + 6,
          },
          style: {
            fill: node.bg.hover.color,
            textFill: node.text.hover.color
          }
        })

        if (topology.toolTip) {
          topology.toolTip.attr({
            shape: {
              x: node.x,
              y: node.y - 50,
              width: node.width,
              height: node.height
            },
            style: {
              text: node.text.title
            }
          })

          topology.toolTip.show();
        }
      })

      graph.on('mouseout', function(params) {
        graph.attr({
          shape: {
            x: node.x,
            y: node.y,
            width: node.width,
            height: node.height,
          },
          style: {
            fill: node.bg.color,
            textFill: node.text.color
          }
        })
        if (topology.toolTip) {
          topology.toolTip.hide();
        }
      })

      group.add(graph);
    });
  }

  function makeTip(topology, group) {
    var toolTip = new zrender.Rect({
      shape: {
        x: 0,
        y: 0,
        width: 0,
        height: 0,
        r: [5, 5, 5, 5]
      },
      style: {
        fill: '#1f1f1f',
        text: '',
        textFill: '#fff',
        textPosition: 'inside',
        textAlign: 'center',
        textBaseline: 'middle',
        opacity: '0.9'
      }
    });
    group.add(toolTip);
    topology.toolTip = toolTip;
  }

  function makeVectorLines(topology, group, data) {
    var sourceNode, targetNode;
    _.forEach(data, function(node) {
      _.forEach(node.sourceNodeIds, function(sourceNodeId) {
        sourceNode = _.find(data, { nodeId: sourceNodeId });
        targetNode = node;
        makeLine(topology, group, sourceNode, targetNode);
      });

      _.forEach(node.targetNodeIds, function(targetNodeId) {
        sourceNode = node;
        targetNode = _.find(data, { nodeId: targetNodeId });
        makeLine(topology, group, sourceNode, targetNode);
      });
    });
  }

  function makeLine(topology, group, sourceNode, targetNode) {
    var arrowRadius = 5,
      borderWidth = 2;
    var xStart = 0,
      yStart = 0,
      xEnd = 0,
      yEnd = 0,
      arrowX = 0,
      arrowY = 0,
      arrowRotation = 0;
    var line;
    if (sourceNode && targetNode) {

      var arrowDirection="";

      if (sourceNode.y > targetNode.y) {
        xStart = sourceNode.x + sourceNode.width;
        yStart = sourceNode.y + sourceNode.height / 2;
        xEnd = targetNode.x + targetNode.width;
        yEnd = targetNode.y + targetNode.height / 2;

        arrowX = xEnd;
        arrowY = yEnd + arrowRadius / 2 + borderWidth;

        var  kink = xEnd>xStart?(xEnd+20):(xStart+20);
        line = new zrender.Polyline({
          shape: {
            points: [
              [xStart, yStart],
              [kink, yStart],
              [kink, yEnd],
              [xEnd, yEnd]
            ]
          },
          style: {
            stroke: '#777',
            lineWidth: 1
          }
        });
        arrowDirection = "left"
      } else if (sourceNode.y < targetNode.y) {
        xStart = sourceNode.x + sourceNode.width / 2;
        yStart = sourceNode.y + sourceNode.height;
        xEnd = targetNode.x + targetNode.width / 2;
        yEnd = targetNode.y;

        arrowX = xEnd;
        arrowY = yEnd - arrowRadius / 2 - borderWidth;
        arrowRotation = 1.04;
        line = new zrender.Polyline({
          shape: {
            points: [
              [xStart, yStart],
              [xStart, yStart + ((yEnd - yStart) / 2)],
              [xEnd, yEnd - ((yEnd - yStart) / 2)],
              [xEnd, yEnd]
            ]
          },
          style: {
            stroke: '#777',
            lineWidth: 1
          }
        });
      } else if (sourceNode.x > targetNode.x) {
        xStart = sourceNode.x;
        yStart = sourceNode.y + sourceNode.height / 2;
        xEnd = targetNode.x + targetNode.width;
        yEnd = targetNode.y + targetNode.height / 2;

        arrowX = xEnd + arrowRadius / 2 + borderWidth;
        arrowY = yEnd;
        arrowRotation = 1.58;
        line = new zrender.Polyline({
          shape: {
            points: [
              [xStart, yStart],
              [xStart, yStart + ((yEnd - yStart) / 2)],
              [xEnd, yEnd - ((yEnd - yStart) / 2)],
              [xEnd, yEnd]
            ]
          },
          style: {
            stroke: '#777',
            lineWidth: 1
          }
        });
      } else if (sourceNode.x < targetNode.x) {
        xStart = sourceNode.x + sourceNode.width;
        yStart = sourceNode.y + sourceNode.height / 2;
        xEnd = targetNode.x;
        yEnd = targetNode.y + targetNode.height / 2;

        arrowX = xEnd - arrowRadius / 2 - borderWidth;
        arrowY = yEnd;
        arrowRotation = 0.52;
        line = new zrender.Polyline({
          shape: {
            points: [
              [xStart, yStart],
              [xStart, yStart + ((yEnd - yStart) / 2)],
              [xEnd, yEnd - ((yEnd - yStart) / 2)],
              [xEnd, yEnd]
            ]
          },
          style: {
            stroke: '#777',
            lineWidth: 1
          }
        });
      } else {
        return;
      }

      group.add(line);


      var points;

      if(arrowDirection == "left"){
        points = [[arrowX+6,arrowY-8],[arrowX+6,arrowY],[arrowX,arrowY-4]]
      }else{
        points = [[arrowX-3,arrowY],[arrowX+3,arrowY],[arrowX,arrowY+5]]
      }

      var arrow = new zrender.Polygon({
        shape: {
          points:points
        }
      });
      group.add(arrow);


      line.on('mouseover', mouseover);
      line.on('mouseout', mouseout);
      arrow.on('mouseover', mouseover)
      arrow.on('mouseout', mouseout)

      function mouseover(params) {
        this.attr({
          style: {
            stroke: '#f90',
            lineWidth: 2
          }
        })
        if (topology.toolTip) {
          topology.toolTip.attr({
            shape: {
              x: params.event.offsetX,
              y: params.event.offsetY,
              width: 200,
              height: 35
            },
            style: {
              text: sourceNode.text.title + ' > ' + targetNode.text.title
            }
          })
          topology.toolTip.show();
        }
      }

      function mouseout() {
        this.attr({
          style: {
            stroke: '#777',
            lineWidth: 1
          }
        })

        if (topology.toolTip) {
          topology.toolTip.hide();
        }
      }

    }
  }

  function addDragEvent(zr, topology) {
    var moveStatus = false,
      lastMouseDownPosition;

    zr.on('mousedown', function(e) {
      lastMouseDownPosition = [e.event.zrX, e.event.zrY];
      moveStatus = true;
    });

    zr.on('mouseup', function(e) {
      moveStatus = false;
    });

    zr.on('mousemove', function(e) {
      if (moveStatus) {
        var xdiff = e.event.zrX - lastMouseDownPosition[0];
        var ydiff = e.event.zrY - lastMouseDownPosition[1];

        var els = zr.storage.getDisplayList(true, true);
        for (var i = 0; i < els.length; i++) {
          var el = els[i];

          el.position[0] = el.position[0] + xdiff;
          el.position[1] = el.position[1] + ydiff;
          el.dirty();
        }
        zr.refresh();
        lastMouseDownPosition = [e.event.zrX, e.event.zrY];
      }
    });
  }

  function addZoomEvent(zr, topology) {
    var scale = 1.0;
    zr.on('mousewheel', function(e) {
      var delta = e.wheelDelta;
      var newScale = scale + delta / 10.0;

      var xdiff = e.event.zrX;
      var ydiff = e.event.zrY;

      if (newScale < 0.5 || newScale > 3) {
        return;
      }
      scale = newScale;

      topology.group.animateTo({
        scale: [scale, scale],
      }, 100, 100)

      zr.refresh();
    });
  }

  window.topology = topology;
})(window);